當呼叫 setState 後會發起 component re-render,React 會啟動 reconciliation 流程,最終更新瀏覽器的 DOM 並反映在畫面上。這個過程分為兩個主要階段:Render phase 和 Commit phase。component 內的子孫 component 也會連帶觸發 re-render:
呼叫 setState 更新資料
當呼叫 setState 時,React 並不會立即更新狀態。setState 是一個非同步操作,React 會將這個狀態更新加入一個任務隊列中,並且會自動將多個 setState 操作合併進行批次更新(batch update)。
檢查 state 是否有變化
React 會執行 Object.is(),檢查 state 新舊資料是否有變化。
Object.is() 拿掉會影響 reconciliation 嗎?並不會,Object.is()就只是個讓效能優化、減少 re-render 的檢查機制,沒有使用 Object.is() 檢查 state 就是直接讓 component 直接進行 re-render。
// This is a React Quiz from BFE.dev 
import * as React from 'react'
import { useState, useEffect} from 'react'
import { createRoot } from 'react-dom/client'
function A() {
  console.log('A')
  return <B/>
}
function B() {
  console.log('B')
  return <C/>
}
function C() {
  console.log('C')
  return null
}
function D() {
  console.log('D')
  return null
}
function App() {
  const [state, setState] = useState(0)
  useEffect(() => {
    setState(state => state + 1)
  }, [])
  console.log('App')
  return (
    <div>
      <A state={state}/>
      <D/>
    </div>
  )
}
const root = createRoot(document.getElementById('root'));
root.render(<App/>)
    
1.App component 初始渲染:App component 掛載到瀏覽器後,
console.log('App')
<A state={state}/> 元件,此時的state 0 作為props 傳入子元件<A>
console.log('A')
B/> 元件console.log('B')
<C/> 元件console.log('C')
App  繼續渲染 <D/> 元件console.log('D')
App re-rendersetState(state => state + 1),state 的原始資料是 0,setState更新值後 state 為 1,資料經 object.is() 檢查後發現有更新故觸發 re-render,重新根據新props 跟 state 產生新的 App 元件。console.log('App')
<A state={state}/> 元件,此時的state 1 作為props 傳入子元件<A>
console.log('A')
B/> 元件console.log('B')
<C/> 元件console.log('C')
App  繼續渲染 <D/> 元件console.log('D')
'App'
'A'
'B'
'C'
'D'
'App'
'A'
'B'
'C'
'D'